package stef.home.arp.labos1.matrix;

import java.math.BigDecimal;

import stef.home.arp.labos1.utils.Utils;

public class Op {
	
	public static int ADD = 1;
	public static int SUB = 2;
	public static int EQADD = 3;
	public static int EQSUB = 4;
	public static int LU = 5;
	public static int LUP = 6;
	public static double e = 0.00000001;
	
	public static Matrix multipyMatrix(Matrix A, Matrix B){
		int columns = A.getNcolumns();
		int rows = B.getNrows();
		if(columns != rows){
			throw new IllegalArgumentException("Dimensions do not match!");
		}
		BigDecimal[][] C = new BigDecimal[columns][rows];
		double[][] c = new double[columns][rows];
		for(int i = 0; i< A.getNrows(); i++)
			for(int j = 0; j<B.getNcolumns(); j++){
				C[i][j] = new BigDecimal(0);
				for(int k = 0; k<columns;k++){
					C[i][j] = C[i][j].add( new BigDecimal(A.getMatrix()[i][k]).multiply(new BigDecimal(B.getMatrix()[k][j])));  
				}
			}
		for(int i = 0; i< A.getNrows(); i++)
			for(int j = 0; j<B.getNcolumns(); j++)
				c[i][j] = C[i][j].doubleValue();
		return new Matrix(A.getNrows(),B.getNcolumns(),c);
	}
	
	public static Matrix scalarMultiplication(double c, Matrix A){
		int columns = A.getNcolumns();
		int rows = A.getNrows();
		double[][] C = new double[rows][columns];
		for(int i = 0; i< A.getNcolumns();i++)
			for(int j = 0; j<A.getNrows();j++)
				C[j][i] = new BigDecimal(A.getMatrix()[j][i]).multiply(new BigDecimal(c)).doubleValue();
		return new Matrix(rows, columns, C);		
	}
	
	public static Matrix transposeMatrix(Matrix A){
		int columns = A.getNcolumns();
		int rows = A.getNrows();
		double[][] C = new double[columns][rows];
		for(int i = 0; i< columns;i++)
			for(int j = 0; j < rows;j++)
				C[i][j] = A.getMatrix()[j][i];
		return new Matrix(columns, rows, C);
	}
	
	public static Matrix additionMatrix(Matrix A, Matrix B, int mode){
		if(A.getNcolumns() != B.getNcolumns() && A.getNrows() != B.getNrows()){
			throw new IllegalArgumentException("Dimensions do not match!");
		}
		boolean add = false;
		if (mode == 1 || mode == 3)
			add = true;
		int columns = A.getNcolumns();
		int rows = A.getNrows();
		double[][] C;
		C = new double[rows][columns];
		for(int i = 0; i< columns; i++)
			for(int j = 0; j<rows;j++)
				if(add)
					C[j][i] = new BigDecimal(A.getMatrix()[j][i]).add(new BigDecimal(B.getMatrix()[j][i])).doubleValue();
				else 
					C[j][i] = new BigDecimal(A.getMatrix()[j][i]).subtract(new BigDecimal(B.getMatrix()[j][i])).doubleValue();
		return new Matrix(rows, columns, C);
	}
	
	public static int forwardSupstitution(Matrix A, Matrix b){
		int n = b.getNrows();
		for(int i = 0 ; i < n-1 ; i++)
			for(int j = i+1; j<n; j++)
				b.getMatrix()[j][0] -= A.getMatrix()[j][i] * b.getMatrix()[i][0];
		return 0;
	}
	
	
	public static int backwardSupstitution(Matrix A, Matrix b){
		int n = b.getNrows();
		for(int i = n-1; i>=0;i--){
			if(Math.abs(A.getMatrix()[i][i]) < e){
				System.out.println("One solution is free");
				return -1;
			}
			b.getMatrix()[i][0] /= A.getMatrix()[i][i];
			for(int j = 0;j < i; j++)
				b.getMatrix()[j][0] -= A.getMatrix()[j][i]*b.getMatrix()[i][0];
		}
		return 0;
	}
	
	public static int LUDecomposition(Matrix A){
		int n = A.getNrows();
		for(int i = 0;i < n-1 ;i++){
			if(Math.abs(A.getMatrix()[i][i]) < e){
				System.out.println("Matrix isn't singular");
				return -1;
			}
			for(int j = i+1; j< n; j++){
				A.getMatrix()[j][i] /= A.getMatrix()[i][i];
				for(int k = i+1; k<n; k++)
					A.getMatrix()[j][k] -= A.getMatrix()[j][i]* A.getMatrix()[i][k];
			}
		}
		return 0;
	}
	
	public static int LUPDecomposition(Matrix A,Matrix P){
		int n = A.getNrows();
		int r;
		for(int i = 0;i < n-1 ;i++){
			r = A.findMax(i, i);
			if(r != i){
				A.swapRows(i, r);
				P.swapRows(i, r);
			}
			if(Math.abs(A.getMatrix()[i][i]) < e){
				System.out.println("Matrix isn't singular");
				return -1;
			}
			for(int j = i+1; j< n; j++){
				A.getMatrix()[j][i] /= A.getMatrix()[i][i];
				for(int k = i+1; k<n; k++)
					A.getMatrix()[j][k] -= A.getMatrix()[j][i]* A.getMatrix()[i][k];
			}
		}
		return 0;
	}
	
	public static void solveLU(Matrix A, Matrix b, int mode){
		int ret;
		Matrix P = null;
		if (mode == LU)
			ret = LUDecomposition(A);
		else{
			P = Utils.createIdentityMatrix(A.getNrows());
			ret = LUPDecomposition(A, P);
		}
		if(ret != -1){
			System.out.println("A:");
			Utils.printMatrix(A);
			if(mode == LUP)
				try{
					b = multipyMatrix(P, b);
				}catch (IllegalArgumentException e) {
					System.out.println(e.getMessage());
					return;
				}
			Op.forwardSupstitution(A, b);
			System.out.println("y:");
			Utils.printMatrix(b);
			ret = Op.backwardSupstitution(A, b);
			if(ret != -1){
				System.out.println("x:");
				Utils.printMatrix(b);
			}
		}
	}
	
	public static Matrix inverz(Matrix A){
		Matrix P = Utils.createIdentityMatrix(A.getNcolumns());
		
		Op.LUPDecomposition(A, P);
		int ret;
		for(int i = 0; i<A.getNcolumns();i++){	
			Matrix E = P.getColumn(i);
//			Utils.printMatrix(E);
			Op.forwardSupstitution(A, E);
			ret = Op.backwardSupstitution(A, E);
			if(ret == -1){
				System.out.println("ne mogu se pronac rijesenja");
			}
			P.setColum(i, E);
		}
		return P;
	}
}
